/**
 * The MIT License	
 * 
 * Copyright (c) 2009 Ghost Interactive
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * 
 * ghostinteractive@gmail.com
 * http://ghostinteractive.net/blog
 * @author Jeremy brown
 */

package net.ghostinteractive.overlays
{
	import flash.display.*;
	import flash.events.*;
	import flash.geom.*;
	
	import org.openzoom.flash.components.MultiScaleImage;
	import org.openzoom.flash.descriptors.IMultiScaleImageDescriptor;
	import org.openzoom.flash.events.ViewportEvent;
	
	public class HotspotLayer extends Sprite
	{
		// --Protected
		protected var _hotspots	:Array;
		protected var _image	:MultiScaleImage

		/**
	 	 * Constructor
	 	 * HotspotLayer provides a means of adding a layer of "hotspot" elements to an OpenZoom MultiScaleImage.
	 	 * 
	 	 * @param MultiScaleImage to sync this layer with
		 */
		public function HotspotLayer( image:MultiScaleImage = null )
		{
			super();
			
			_hotspots = [];
			
			if( image ) { multiScaleImage = image; }
		}
		
		// -----------------------------------------------------
		//
		//	PUBLIC
		//	
		// -----------------------------------------------------
		
		/**
		 * Setting this param essentially syncs the HotspotLayer to a MultiScaleImage instance 
		 * by aligning and setting it's size to the same dimensions as the image's viewport and
		 * adding event listeners to the image's viewport
		 * Calls purge() method to remove listeners to previous MultiScaleImage and any Hotspots 
		 * previously in use
		 * 
		 * @param _image MultiScaleImage instance for HotspotLayer to sync with
		 */
		public function set multiScaleImage( image:MultiScaleImage ):void
		{
			if( _image ) { purge() };
			
			_image 	= image;
			x 		= _image.x;
			y 		= _image.y;
	
			setActualSize( _image.viewport.viewportWidth, _image.viewport.viewportHeight );
			
			addListeners();
		}
		
		/**
		 * Adds hotspot item to layer
		 */
		public function addHotspot( hotspot:Hotspot ):void { _hotspots.push( addChild( hotspot ) ); }
		
		/**
		 * Removes hotspot from layer
		 */
		public function removeHotspot( hotspot:Hotspot ):Hotspot
		{
			var index:int = _hotspots.indexOf( hotspot );
			if( index >= 0 ) {
				hotspot.destroy();
				if( hotspot.parent ) {
					_hotspots.splice( index, 1 );
					hotspot.parent.removeChild( hotspot );
					return hotspot;
				}
			}
			return null;
		}
		
		/**
		 * Removes all hotspots
		 */
		public function removeAllHotspots():void
		{
			for each ( var hotspot:Hotspot in _hotspots ) {
				hotspot.destroy();
				if( hotspot.parent ) { 
					hotspot.parent.removeChild( hotspot ) 
				};
			}
			_hotspots.splice( 0 );
		}
		
		/**
		 * Moves the hotspot to the top of the display list
		 */
		public function moveToHighestDepth( hotspot:Hotspot ):void
		{
			if( hotspot.parent ) { hotspot.parent.addChild( hotspot ) }
		}
		
		/**
		 * Removes all hotspots and listeners, calling this function will render
		 * the hotspot layer useless until the multiScaleImage property is once again set
		 */
		public function purge():void
		{
			removeAllHotspots();
			removeListeners();
		}
		
		/**
		 * Disables all hotspots from being clickable
		 * This method is called when the image is panning, dragging or zooming
		 */
		public function disableHotspots():void
		{
			mouseChildren = false;
			mouseEnabled = false;
			for each( var hotspot:Hotspot in _hotspots ) {
				hotspot.mouseEnabled = false;
			}
		}
		
		/**
		 * Enables all hotspots
		 * This method is called when image panning, dragging or zooming is complete
		 */
		public function enableHotspots():void
		{
			mouseChildren = true;
			mouseEnabled = true;
			for each( var hotspot:Hotspot in _hotspots ) {
				hotspot.mouseEnabled = true
			}
		}
		
		/**
		 * Pans the vertical and horizontal center of the viewport to an instance 
		 * or sub class instance of the Hotspots class. Additionally, this method
		 * will account for values assigned to the xoffset and yoffset of the hotspot object
		 * 
		 * @param hotspot Hotspot or sub class to pan to
		 * @param duration Duration of pan ( not yet implemented ) set to 0 for immediate pan
		 */
		public function panToHotspot( hotspot:Hotspot, duration:Number = 1 ):void
		{
			var xoffset:Number = -( hotspot.xoffset * _image.viewport.scale );
			var yoffset:Number = -( hotspot.yoffset * _image.viewport.scale );
			var sceneLoc:Point = _image.localToScene( new Point( hotspot.x + xoffset, hotspot.y + yoffset ) );
			var xtarg:Number = ( sceneLoc.x / _image.sceneWidth ) - ( _image.viewportWidth / 2 );
			var ytarg:Number = ( sceneLoc.y / _image.sceneHeight ) - ( _image.viewportHeight / 2 );
			_image.panTo( xtarg, ytarg, duration <= 0 );
		}
		
		/**
		 * Pans the vertical and horizontal center of the viewport to an instance
		 * or sub class instance of the Hotspot class
		 * Zooms the viewport to the depth specified by hotspot param
		 * 
		 * @param hotspot Hotspot or sub class to pan and zoom to
		 * @param duration Duration of pan and zoom ( not yet implemented ) set to 0 for immediate pan
		 */
		public function panAndZoomToHotspot( hotspot:Hotspot, duration:Number = 1 ):void
		{
			var xoffset:Number = -( hotspot.xoffset * _image.viewport.scale );
			var yoffset:Number = -( hotspot.yoffset * _image.viewport.scale );
			var sceneLoc:Point = _image.localToScene( new Point( hotspot.x + xoffset, hotspot.y + yoffset ) );
			var xtarg:Number = sceneLoc.x / _image.sceneWidth;
			var ytarg:Number = sceneLoc.y / _image.sceneHeight;
			_image.viewport.zoomTo( hotspot.zoom, .5, .5, duration <= 0 );
			_image.viewport.panCenterTo( xtarg, ytarg, duration <= 0 );
		}
		
		/**
		 * Performs layout operation
		 * Should be called once after all items have been added to layer
		 */
		public function validateNow():void { onTransformUpdate(); }
		
		/**
		 * Applies a scroll rect to hotspot layer
		 */
		public function setActualSize( w:Number, h:Number ):void { scrollRect = new Rectangle( 0, 0, w, h ); }
		
		// -----------------------------------------------------
		//
		//	PROTECTED
		//	
		// -----------------------------------------------------
		
		// -----------------------------------------------
		//	Event Handlers
		// -----------------------------------------------
		
		/**
		 *  Keeps the hotspots at their relative positions
		 */
		protected function onTransformUpdate( event:Event = null ):void
		{
			if( !_image.source ) return; 
			var w:Number = IMultiScaleImageDescriptor( _image.source ).width;
			var h:Number = IMultiScaleImageDescriptor( _image.source ).height;
			var xPerc:Number;
			var yPerc:Number;
			var loc:Point = new Point();
			var targ:Point = new Point();
			
			for each( var hotspot:Hotspot in _hotspots )
			{
				xPerc = hotspot.position.x / w;
				yPerc = hotspot.position.y / h;
				
				loc.x 	= _image.sceneWidth * xPerc;
				loc.y 	= _image.sceneHeight * yPerc;
				targ 	= _image.sceneToLocal( loc );
			
				hotspot.x = targ.x;
				hotspot.y = targ.y;
			}
		}
		
		protected function onImageMouseUp( event:MouseEvent ):void { enableHotspots(); }
		
		protected function onTransformStart( event:Event ):void { disableHotspots(); }
		
		protected function onTransormEnd( event:ViewportEvent ):void { enableHotspots(); }
		
		protected function removeListeners():void
		{
			_image.viewport.removeEventListener( ViewportEvent.TRANSFORM_START, onTransformStart );
			_image.viewport.removeEventListener( ViewportEvent.TRANSFORM_END, onTransormEnd );
			_image.viewport.removeEventListener( ViewportEvent.TRANSFORM_UPDATE, onTransformUpdate );
			_image.removeEventListener( MouseEvent.MOUSE_UP, onImageMouseUp );
		}
		
		protected function addListeners():void
		{
			_image.viewport.addEventListener( ViewportEvent.TRANSFORM_START, onTransformStart );
			_image.viewport.addEventListener( ViewportEvent.TRANSFORM_END, onTransormEnd );
			_image.viewport.addEventListener( ViewportEvent.TRANSFORM_UPDATE, onTransformUpdate );
			_image.addEventListener( MouseEvent.MOUSE_UP, onImageMouseUp );
		}
	}
}